// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.


`include "espi_header.iv"

`timescale 1 ps / 1 ps
module espi_pc_channel
  #(
    parameter DEVICE_FAMILY      = "MAX 10 FPGA",
    parameter PC_TXFIFO_SIZE     = 2048,
    parameter PC_TXFIFO_WIDTHU   = 11,
    parameter PC_RXFIFO_SIZE     = 2048,
    parameter PC_RXFIFO_WIDTHU   = 11,
    parameter HDRBYTE_ARR        = 3,
    parameter DATABYTE_ARR       = 2
    )
    (
     input               clk,
     input               reset_n,
     input               pltrst_n,
     input               pec_mismatch,
     input               pc_error_condition,
     input               flush_pc_fifo,
     input               pc_channel_reset,
     input               stop_det,
     input               pc_channel_en,
     input [7:0]         txfifo_wdata,
     input [1:0]         rx_hdr_array_cnt,
     input [2:0]         rx_data_array_cnt,
     input [7:0]         header_byte[HDRBYTE_ARR],
     input [7:0]         data_byte[DATABYTE_ARR],
     input [7:0]         command_byte,
     input               rx_detect_data,
     input               rx_detect_header,
     input               rx_detect_header_end,
     input               rx_detect_data_end,
     input               rx_detect_crc_end,
     input               rx_detect_command,
     input               tx_gen_command,
     input               tx_gen_header,
     input               tx_gen_data,
     input               spiclk_cnt_done_hl,
     input               spiclk_cnt_done_lh,
     input               detect_put_np_rxfifo,
     input               detect_put_pc_rxfifo,
     input               detect_get_np_txfifo,
     input               detect_get_pc_txfifo,
     input               detect_memrd_short,
     input               detect_memwr_short,
     input               detect_iord_short,
     input               detect_iowr_short,
     input               detect_short_1b,
     input               detect_short_2b,
     input               detect_short_4b,
     input               write_pc_txfifo,
     input               write_np_txfifo,
     input               np_connected,
     output logic        pc_free,
     output logic        np_free,
     output logic        pc_rxfifo_avail,
     output logic        np_rxfifo_avail,
     output logic        pc_channel_ready,
     output logic [7:0]  pc_rxfifo_rdata,
     output logic [7:0]  np_rxfifo_rdata,
     output logic [7:0]  txfifo_rdata,
     output logic        get_pc_txfifo,
     output logic        get_np_txfifo,

     output logic [31:0] pc_short_data,
     output logic        pc_short_data_valid,
     output logic        np_txfifo_avail,
     output logic        pc_txfifo_avail,

     output logic [31:0] mem_address,
     output logic        mem_write,
     output logic [31:0] mem_writedata,
     output logic [3:0]  mem_byteenable,
     output logic        mem_read,
     input logic [31:0]  mem_readdata,
     input logic         mem_waitrequest,

     output logic [15:0] io_address,
     output logic        io_write,
     output logic [7:0]  io_writedata,
     output logic        io_read,
     input logic [7:0]   io_readdata,
     input logic         io_waitrequest
     );

    logic        channel_reset_n;

    // Posted Receive FIFO
    logic        pc_rxfifo_put;
    logic [7:0]  pc_rxfifo_wdata;
    logic        pc_rxfifo_get;
    logic        pc_rxfifo_rst_n;
    logic        pc_rxfifo_full;
    logic        pc_rxfifo_empty;
    logic        pc_rxfifo_reset;

    // Non-Posted Transmit FIFO
    logic        put_np_rxfifo;
    logic        np_txfifo_empty;
    logic        np_txfifo_full;
    logic [7:0]  np_txfifo_rdata;

    // Non-Posted Receive FIFO
    logic        np_rxfifo_empty;
    logic        np_rxfifo_get;
    logic [7:0]  np_rxfifo_wdata;
    logic        np_rxfifo_reset;

    // Completion Transmit FIFO - MEMRD32
    logic        put_np_pc_txfifo_req;
    logic        put_np_pc_txfifo_gnt;
    logic        put_np_pc_txfifo_put;
    logic [7:0]  put_np_pc_txfifo_wdata;
    logic        put_np_pc_txfifo_avail;

    // Completion Transmit FIFO - Short Completion (IORD, MEMRD32)
    logic        short_cpl_pc_txfifo_req;
    logic        short_cpl_pc_txfifo_gnt;
    logic        short_cpl_pc_txfifo_put;
    logic [7:0]  short_cpl_pc_txfifo_wdata;
    logic        short_cpl_pc_txfifo_avail;

    // Completion Transmit FIFO
    logic        pc_txfifo_put;
    logic [7:0]  pc_txfifo_wdata;
    logic        pc_txfifo_full;
    logic [7:0]  pc_txfifo_rdata;
    logic        pc_txfifo_empty;

    // Avalon Memory-Mapped Master to memory
    logic        put_np_mem_req;
    logic        put_np_mem_gnt;
    logic [31:0] put_np_mem_address;
    logic        put_np_mem_read;
    
    // PUT_PC Avalon Memory-Mapped Memroy Master
    logic        put_pc_mem_req;
    logic        put_pc_mem_gnt;
    logic [31:0] put_pc_mem_address;
    logic        put_pc_mem_write;
    logic [31:0] put_pc_mem_writedata;
    logic [3:0]  put_pc_mem_byteenable;

     // MEM_SHORT Avalon Memory-Mapped Memory Master
    logic        mem_short_req;
    logic        mem_short_gnt;
    logic [31:0] mem_short_address;
    logic        mem_short_write;
    logic [31:0] mem_short_writedata;
    logic [3:0]  mem_short_byteenable;
    logic        mem_short_read;

     // Needed for the completion
    logic [1:0]  mem_idx;
    logic pc_txfifo_reset;
    logic np_rxfifo_full;


    assign channel_reset_n = reset_n && pltrst_n && ~pc_channel_reset;

    assign txfifo_rdata = detect_get_pc_txfifo ? pc_txfifo_rdata :
                          detect_get_np_txfifo ? np_txfifo_rdata :
                          {8{1'b0}};
    assign pc_rxfifo_put = detect_put_pc_rxfifo && spiclk_cnt_done_hl &&
                           (rx_detect_command || rx_detect_header || rx_detect_data);
    assign pc_rxfifo_wdata = (rx_detect_command && spiclk_cnt_done_hl) ? command_byte :
                             (rx_detect_header && spiclk_cnt_done_hl) ? header_byte[rx_hdr_array_cnt] :
                             (rx_detect_data && spiclk_cnt_done_hl) ? data_byte[rx_data_array_cnt] :
                             {8{1'b0}};
    assign put_np_rxfifo = detect_put_np_rxfifo && spiclk_cnt_done_hl &&
                           (rx_detect_command || rx_detect_header);
    assign np_rxfifo_wdata = (rx_detect_command && spiclk_cnt_done_hl) ? command_byte :
                             (rx_detect_header && spiclk_cnt_done_hl) ? header_byte[rx_hdr_array_cnt] :
                             {8{1'b0}};

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            np_txfifo_avail  <= 0;
        end
        else begin
            np_txfifo_avail <= ~np_txfifo_empty;
        end
    end

    assign pc_txfifo_avail = (short_cpl_pc_txfifo_avail | put_np_pc_txfifo_avail) & ~np_connected;

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            np_free  <= 1'b1;            // during reset, empty is high, hence np_free is 1
        end
        else begin
            if (np_rxfifo_empty) begin
                np_free <= 1'b1;
            end
            else if (np_rxfifo_avail) begin
                np_free <= 1'b0;
            end
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            pc_free  <= 1'b1;           // during reset, empty is high, hence pc_free is 1
        end
        else begin
            if (pc_rxfifo_empty) begin
                pc_free <= 1'b1;
            end
            else if (pc_rxfifo_avail) begin
                pc_free <= 1'b0;
            end
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            np_rxfifo_avail  <= 1'b0;
        end
        else begin
            if (detect_put_np_rxfifo && rx_detect_crc_end && ~pc_error_condition && ~pec_mismatch) begin
                np_rxfifo_avail <= 1'b1;
            end
            else if (np_rxfifo_empty) begin
                np_rxfifo_avail <= 1'b0;
            end
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            pc_rxfifo_avail  <= 1'b0;
        end
        else begin
            if (detect_put_pc_rxfifo && rx_detect_crc_end && ~pc_error_condition && ~pec_mismatch) begin
                pc_rxfifo_avail <= 1'b1;
            end
            else if (pc_rxfifo_empty) begin
                pc_rxfifo_avail <= 1'b0;
            end
        end // else: !if(!channel_reset_n)
    end // always @ (posedge clk or negedge channel_reset_n)

    always @(posedge clk or negedge channel_reset_n) begin
        if (!channel_reset_n) begin
            pc_channel_ready  <= 1'b0;
        end
        else begin
            pc_channel_ready <= pc_channel_en;
        end
    end

    assign get_pc_txfifo = (detect_get_pc_txfifo & spiclk_cnt_done_lh &
                            (tx_gen_command | tx_gen_header | tx_gen_data) & ~pc_txfifo_empty);
    assign get_np_txfifo = (detect_get_np_txfifo & spiclk_cnt_done_lh &
                            (tx_gen_command | tx_gen_header | tx_gen_data) & ~np_txfifo_empty);

    espi_fifo
      #(
        .DSIZE  (8),
        .DEPTH  (PC_RXFIFO_SIZE),
        .WIDTHU (PC_RXFIFO_WIDTHU),
        .FAMILY (DEVICE_FAMILY)
        )
    pc_rxfifo_inst
      (
       .clk          (clk),
       .rst_n        (pc_rxfifo_rst_n),
       .put          (pc_rxfifo_put),
       .get          (pc_rxfifo_get),
       .wdata        (pc_rxfifo_wdata),
       .full         (pc_rxfifo_full),
       .empty        (pc_rxfifo_empty),
       .rdata        (pc_rxfifo_rdata),
       .usedw        ()
       );

    assign pc_rxfifo_rst_n = channel_reset_n & ~flush_pc_fifo & ~pc_rxfifo_reset;

    espi_fifo
      #(
        .DSIZE  (8),
        .DEPTH  (PC_TXFIFO_SIZE),
        .WIDTHU (PC_TXFIFO_WIDTHU),
        .FAMILY (DEVICE_FAMILY)
        )
    pc_txfifo_inst
      (
       .clk          (clk),
       .rst_n        (~pc_txfifo_reset),
       .put          (pc_txfifo_put),
       .get          (get_pc_txfifo),
       .wdata        (pc_txfifo_wdata),
       .full         (pc_txfifo_full),
       .empty        (pc_txfifo_empty),
       .rdata        (pc_txfifo_rdata),
       .usedw        ()
       );

    assign pc_txfifo_put = short_cpl_pc_txfifo_gnt ? short_cpl_pc_txfifo_put :
                           put_np_pc_txfifo_gnt ? put_np_pc_txfifo_put :
                           1'b0;

    assign pc_txfifo_wdata = short_cpl_pc_txfifo_gnt ? short_cpl_pc_txfifo_wdata :
                             put_np_pc_txfifo_gnt ? put_np_pc_txfifo_wdata :
                             8'h0;

    assign short_cpl_pc_txfifo_gnt = short_cpl_pc_txfifo_req & ~put_np_pc_txfifo_gnt & channel_reset_n &
                                     ~pc_txfifo_reset;
    assign put_np_pc_txfifo_gnt  = put_np_pc_txfifo_req & ~short_cpl_pc_txfifo_req & channel_reset_n;

    logic       np_short_command;
    
    assign pc_txfifo_reset = (np_connected & np_short_command) | ~channel_reset_n;
    assign np_short_command = detect_memrd_short | detect_iord_short | detect_iowr_short;

    logic       np_txfifo_reset;
    logic       np_txfifo_put   = 0;
    logic [7:0] np_txfifo_wdata = 0;

    assign mem_address    = mem_short_gnt  ? mem_short_address : 
                            put_pc_mem_gnt ? put_pc_mem_address :
                            put_np_mem_gnt ? put_np_mem_address :
                            32'h0;
    assign mem_write      = mem_short_gnt  ? mem_short_write :
                            put_pc_mem_gnt ? put_pc_mem_write :
                            1'b0;
    assign mem_writedata  = mem_short_gnt  ? mem_short_writedata :
                            put_pc_mem_gnt ? put_pc_mem_writedata :
                            32'h0;
    assign mem_byteenable = mem_short_gnt  ? mem_short_byteenable :
                            put_pc_mem_gnt ? put_pc_mem_byteenable :
                            4'h0;
    assign mem_read       = mem_short_gnt  ? mem_short_read :
                            put_np_mem_gnt ? put_np_mem_read :
                            1'b0;

    assign mem_short_gnt  = mem_short_req  & ~(put_pc_mem_gnt | put_np_mem_gnt) & channel_reset_n;
    assign put_pc_mem_gnt = put_pc_mem_req & ~(mem_short_req  | put_np_mem_gnt) & channel_reset_n;
    assign put_np_mem_gnt = put_np_mem_req & ~(mem_short_req  | put_pc_mem_req) & channel_reset_n;
   
    espi_fifo
      #(
        .DSIZE  (8),
        .DEPTH  (PC_TXFIFO_SIZE),
        .WIDTHU (PC_TXFIFO_WIDTHU),
        .FAMILY (DEVICE_FAMILY)
        )
    np_txfifo_inst
      (
       .clk          (clk),
       .rst_n        (~np_txfifo_reset),
       .put          (np_txfifo_put),
       .get          (get_np_txfifo),
       .wdata        (np_txfifo_wdata),
       .full         (np_txfifo_full),
       .empty        (np_txfifo_empty),
       .rdata        (np_txfifo_rdata),
       .usedw        ()
       );

    assign np_txfifo_reset = ~channel_reset_n;

    logic [31:0] pc_mem_data;
    logic        pc_mem_data_valid;
    logic [31:0] pc_io_data;
    logic        pc_io_data_valid;

    assign pc_short_data = detect_memrd_short ? pc_mem_data : pc_io_data;
    assign pc_short_data_valid = pc_mem_data_valid | pc_io_data_valid;

    logic rx_detect_crc_end_1;

    espi_pc_io_short_fsm
      #(
        .HEADER_BYTES (HDRBYTE_ARR),
        .DATA_BYTES   (DATABYTE_ARR)
        )
    io_short_fsm
      (
       .detect_iord_short  (detect_iord_short),
       .detect_iowr_short  (detect_iowr_short),
       .detect_short_1b    (detect_short_1b),
       .detect_short_2b    (detect_short_2b),
       .detect_short_4b    (detect_short_4b),
       .rx_detect_crc_end  (rx_detect_crc_end_1),
       .header_byte        (header_byte),
       .data_byte          (data_byte),
       .stop_det           (stop_det),
       .pc_error_condition (pc_error_condition),
       .io_address         (io_address),
       .io_write           (io_write),
       .io_writedata       (io_writedata),
       .io_read            (io_read),
       .io_readdata        (io_readdata),
       .io_waitrequest     (io_waitrequest),
       .pc_io_data         (pc_io_data),
       .pc_io_data_valid   (pc_io_data_valid),
       .clk                (clk),
       .channel_reset_n    (channel_reset_n)
       );

    espi_pc_mem_short_fsm
      #(
        .HEADER_BYTES (HDRBYTE_ARR),
        .DATA_BYTES   (DATABYTE_ARR)
        )
    mem_short_fsm
      (
       .detect_memrd_short (detect_memrd_short),
       .detect_memwr_short (detect_memwr_short),
       .detect_short_1b    (detect_short_1b),
       .detect_short_2b    (detect_short_2b),
       .detect_short_4b    (detect_short_4b),
       .rx_detect_crc_end  (rx_detect_crc_end_1),
       .stop_det           (stop_det),
       .header_byte        (header_byte),
       .data_byte          (data_byte),
       .pc_error_condition (pc_error_condition),
       .mem_req            (mem_short_req),
       .mem_gnt            (mem_short_gnt),
       .mem_address        (mem_short_address),
       .mem_write          (mem_short_write),
       .mem_writedata      (mem_short_writedata),
       .mem_byteenable     (mem_short_byteenable),
       .mem_read           (mem_short_read),
       .mem_readdata       (mem_readdata),
       .mem_waitrequest    (mem_waitrequest),
       .mem_idx            (mem_idx),
       .pc_mem_data_valid  (pc_mem_data_valid),
       .pc_mem_data        (pc_mem_data),
       .clk                (clk),
       .channel_reset_n    (channel_reset_n)
       );

    espi_pc_short_completion_fsm short_cpl_fsm
      (
       .rx_detect_command   (rx_detect_command),
       .spiclk_cnt_done_hl  (spiclk_cnt_done_hl),
       .detect_iowr_short   (detect_iowr_short),
       .detect_iord_short   (detect_iord_short),
       .detect_memrd_short  (detect_memrd_short),
       .detect_short_1b     (detect_short_1b),
       .detect_short_2b     (detect_short_2b),
       .detect_short_4b     (detect_short_4b),
       .io_read             (io_read),
       .io_waitrequest      (io_waitrequest),
       .io_readdata         (io_readdata),
       .mem_idx             (mem_idx),
       .mem_read            (mem_read),
       .mem_waitrequest     (mem_waitrequest),
       .mem_readdata        (mem_readdata),
       .pc_txfifo_req       (short_cpl_pc_txfifo_req),
       .pc_txfifo_gnt       (short_cpl_pc_txfifo_gnt),
       .pc_txfifo_put       (short_cpl_pc_txfifo_put),
       .pc_txfifo_wdata     (short_cpl_pc_txfifo_wdata),
       .pc_txfifo_avail     (short_cpl_pc_txfifo_avail),
       .pc_txfifo_empty     (pc_txfifo_empty),
       .pc_short_data_valid (pc_short_data_valid),
       .clk                 (clk),
       .channel_reset_n     (channel_reset_n)
       );

    espi_pc_put_pc_fsm put_pc_fsm
      (
       .rx_detect_crc_end  (rx_detect_crc_end_1),
       .pc_error_condition (pc_error_condition),
       .pc_rxfifo_empty    (pc_rxfifo_empty),
       .pc_rxfifo_get      (pc_rxfifo_get),
       .pc_rxfifo_rdata    (pc_rxfifo_rdata),
       .pc_rxfifo_reset    (pc_rxfifo_reset),
       .mem_req            (put_pc_mem_req),
       .mem_gnt            (put_pc_mem_gnt),
       .mem_address        (put_pc_mem_address),
       .mem_write          (put_pc_mem_write),
       .mem_writedata      (put_pc_mem_writedata),
       .mem_byteenable     (put_pc_mem_byteenable),
       .mem_waitrequest    (mem_waitrequest),
       .clk                (clk),
       .channel_reset_n    (channel_reset_n)
       );

    logic np_rxfifo_rst_n;
    
    espi_fifo
      #(
        .DSIZE  (8),
        .DEPTH  (PC_RXFIFO_SIZE),
        .WIDTHU (PC_RXFIFO_WIDTHU),
        .FAMILY (DEVICE_FAMILY)
        )
    np_rxfifo_inst
      (
       .clk          (clk),
       .rst_n        (np_rxfifo_rst_n),
       .put          (put_np_rxfifo),
       .get          (np_rxfifo_get),
       .wdata        (np_rxfifo_wdata),
       .full         (np_rxfifo_full),
       .empty        (np_rxfifo_empty),
       .rdata        (np_rxfifo_rdata),
       .usedw        ()
       );

    assign np_rxfifo_rst_n = channel_reset_n & ~flush_pc_fifo & ~np_rxfifo_reset;

    espi_pc_put_np_fsm put_np_fsm
      (
       .np_rxfifo_empty    (np_rxfifo_empty),
       .np_rxfifo_get      (np_rxfifo_get),
       .np_rxfifo_rdata    (np_rxfifo_rdata),
       .np_rxfifo_reset    (np_rxfifo_reset),
       .rx_detect_crc_end  (rx_detect_crc_end_1),
       .np_error_condition (pc_error_condition),
       .pc_txfifo_req      (put_np_pc_txfifo_req),
       .pc_txfifo_gnt      (put_np_pc_txfifo_gnt),
       .pc_txfifo_put      (put_np_pc_txfifo_put),
       .pc_txfifo_wdata    (put_np_pc_txfifo_wdata),
       .pc_txfifo_avail    (put_np_pc_txfifo_avail),
       .pc_txfifo_empty    (pc_txfifo_empty),
       .pc_txfifo_full     (pc_txfifo_full),
       .mem_req            (put_np_mem_req),
       .mem_gnt            (put_np_mem_gnt),
       .mem_address        (put_np_mem_address),
       .mem_read           (put_np_mem_read),
       .mem_readdata       (mem_readdata),
       .mem_waitrequest    (mem_waitrequest),
       .clk                (clk),
       .channel_reset_n    (channel_reset_n)
       );

    always @(posedge clk) begin
        rx_detect_crc_end_1 <= rx_detect_crc_end;
    end

endmodule // espi_pc_channel
